﻿using CommonLibrary;
using CommonLibrary.Models;
using CommonSite.Areas.API.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BLToolkit.Data.Linq;
using CommonSite.Common;
using CommonHelper;
using System.Text;

namespace CommonSite.Areas.API.Controllers
{
    /// <summary>
    /// State: 0 删除 1 未发布 2 已发布
    /// </summary>
    public class FlowController : BaseAPIController
    {
        public ActionResult Query()
        {
            using (CommonDatabase db = new CommonDatabase())
            {
                var query = (from p in db.wf_Flow
                             where p.State != 0
                             select new Flow
                             {
                                 Id = CryptHelper.Encrypt(p.Id),
                                 FormId = CryptHelper.Encrypt(p.Id),
                                 FormName = p.wf_FormInstance.Name,
                                 FlowName = p.Name,
                                 Description = p.Description,
                                 State = p.State
                             }).ToList();

                return Success(new
                {
                    Total = query.Count,
                    Rows = query
                });
            }
        }

        public ActionResult Create(Flow flow)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    using (CommonDatabase db = new CommonDatabase(CommonOption.ReadWrite))
                    {
                        db.BeginTransaction();
                        if (db.wf_Flow.Where(k => k.Name == flow.FlowName && k.State == 1)
                              .Count() > 0)
                        {
                            throw new ArgumentException("流程名称不能重复");
                        }

                        int formId = CryptHelper.DecryptToInt32(flow.FormId);
                        var form = db.wf_Form.Where(k => k.Id == formId && k.State == 1)
                                     .Count();
                        if (form == 0)
                            throw new ArgumentException("参数错误");

                        int formInstanceId = db.SetCommand(
@"insert into wf_FormInstance(TemplateFormId, Name, State, Version)
select Id, Name, State, Version from wf_Form where Id = @id returning Id;",
                                db.Parameter("@id", formId))
                                               .ExecuteScalar<int>();

                        db.SetCommand(
@"insert into wf_FieldInstance(TemplateFieldId, FormId, ControlId, Name, Nullable, Editable, Source, State, Argument, DefaultValue)
  select Id, @form, ControlId, Name, Nullable, Editable, Source, State, Argument, DefaultValue from wf_Field where FormId = @id;",
                                db.Parameter("@form", formInstanceId),
                                db.Parameter("@id", formId))
                          .ExecuteNonQuery();

                        db.wf_Flow.Insert(() => new wf_Flow
                        {
                            Name = flow.FlowName,
                            Description = flow.Description,
                            FormId = formInstanceId
                        });

                        db.CommitTransaction();
                        return Success();
                    }
                }
                catch (Exception ex)
                {
                    return Failed(ex.Message);
                }
            }

            return ArgumentFailed();
        }

        public ActionResult Update(Flow flow)
        {
            if (this.ModelState.IsValid)
            {
                try
                {
                    using (CommonDatabase db = new CommonDatabase(CommonOption.ReadWrite))
                    {
                        db.BeginTransaction();
                        int id = CryptHelper.DecryptToInt32(flow.Id);

                        db.wf_Flow.Where(k => k.Id == id)
                          .Set(k => k.Name, flow.FlowName)
                          .Set(k => k.Description, flow.Description)
                          .Update();

                        db.CommitTransaction();
                        return Success();
                    }
                }
                catch (Exception ex)
                {
                    return Failed(ex.Message);
                }
            }

            return ArgumentFailed();
        }

        public ActionResult Delete(Flow flow)
        {
            try
            {
                using (CommonDatabase db = new CommonDatabase(CommonOption.ReadWrite))
                {
                    db.BeginTransaction();
                    int id = CryptHelper.DecryptToInt32(flow.Id);
                    db.wf_Flow.Where(k => k.Id == id)
                      .Set(k => k.State, 0)
                      .Update();

                    db.CommitTransaction();
                    return Success();
                }
            }
            catch (Exception ex)
            {
                return Failed(ex.Message);
            }
        }

        public ActionResult Publish(Flow input)
        {
            try
            {
                using (CommonDatabase db = new CommonDatabase(CommonOption.ReadWrite))
                {
                    db.BeginTransaction();
                    int id = CryptHelper.DecryptToInt32(input.Id);
                    var flow = db.wf_Flow.Where(k => k.Id == id && k.State == 1).FirstOrDefault();
                    if (flow == null)
                        throw new ArgumentException("流程不存在,或者改流程已经发布。");
                    var form = db.wf_FormInstance.Where(k => k.Id == flow.FormId).First();
                    var fields = db.wf_FieldInstance.Where(k => k.FormId == form.Id).ToList();
                    var builder = new StringBuilder();

                    builder.AppendFormat(
@"create table workflow.form_{0}(
    Id serial primary key", flow.FormId);
                    foreach (var f in fields)
                    {
                        // [name] [type] [nullable] [default]
                        builder.AppendFormat(
                            ", {0} {1} {2}",
                            "field_" + f.Id,
                            GetDataType(f.ControlId),
                            f.Nullable ? "null" : "not null");

                        if (f.ControlId == 10)
                        {
                            CreateFieldTable(db, f);
                        }
                    }

                    builder.Append(");");

                    db.SetCommand(builder.ToString()).ExecuteNonQuery();

                    db.wf_Flow.Where(k => k.Id == id)
                      .Set(k => k.State, 2)
                      .Update();

                    db.CommitTransaction();
                    return Success();
                }
            }
            catch (Exception ex)
            {
                return Failed(ex.Message);
            }
        }

        public ActionResult GetFlowRole(Flow flow)
        {
            try
            {
                using (CommonDatabase db = new CommonDatabase())
                {
                    int id = CryptHelper.DecryptToInt32(flow.Id);
                    string query =
@"select Id, Name, case when RoleId is null then false 
                      else true 
                   end Checked 
from (select * from sys_Role where Id <> 1) t1 
   left join (select RoleId from wf_RoleFlow where FlowId = @flow) t2 on t1.Id = t2.RoleId";
                    var dt = db.SetCommand(query, db.Parameter("@flow", id))
                               .ExecuteDataTable();
                    return Success(new
                    {
                        Total = dt.Rows.Count,
                        Rows = dt
                    });
                }
            }
            catch (Exception ex)
            {
                return Failed(ex.Message);
            }
        }

        public ActionResult SetFlowRole(
            string flow,
            string roles)
        {
            try
            {
                int id = CryptHelper.DecryptToInt32(flow);
                int[] roleId = JSON.ToObject<int[]>(roles);
                using (CommonDatabase db = new CommonDatabase())
                {
                    db.BeginTransaction();
                    if (db.wf_Flow.Where(k => k.Id == id).Count() == 0)
                        throw new ArgumentException("参数错误");

                    db.wf_RoleFlow.Where(k => k.FlowId == id).Delete();
                    if (roleId != null && roleId.Length > 0)
                    {
                        var list = new List<wf_RoleFlow>();
                        foreach (var item in roleId)
                        {
                            list.Add(new wf_RoleFlow() { FlowId = id, RoleId = item });
                        }

                        db.InsertBatch(list);
                    }

                    db.CommitTransaction();
                    return Success();
                }
            }
            catch (Exception ex)
            {
                return Failed(ex.Message);
            }
        }


        /// <summary>
        /// Control:
        /// 1;"文本框";"textbox"
        /// 2;"多行文本框";"textarea"
        /// 3;"日期控件";"date"
        /// 4;"下拉框";"select"
        /// 5;"整数输入框";"digits"
        /// 6;"浮点数输入框";"number"
        /// 7;"隐藏控件";"hidden"
        /// </summary>
        /// <param name="control"></param>
        /// <returns></returns>
        private static string GetDataType(int control)
        {
            switch (control)
            {
                case 1:
                case 2:
                case 8:
                case 11:
                    return "text";

                case 3:
                    return "timestamp";

                case 4:
                case 5:
                case 7:
                    return "int";

                case 6:
                    return "numeric(18, 4)";

                case 9:
                    return "timestamp";

                case 10:
                    return "int";

                default:
                    throw new ArgumentException("control");
            }
        }

        private static void CreateFieldTable(CommonDatabase db, wf_FieldInstance field)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat(
@"create table workflow.field_{0}(
    Id serial primary key,
    Owner int not null", field.Id);

            var columns = db.wf_Column.Where(k => k.TableId == field.Source);
            foreach (var c in columns)
            {
                // [name] [type] [nullable] [default]
                sb.AppendFormat(
                    ", {0} {1} {2}",
                    "column_" + c.Id,
                    GetDataType(c.ControlId),
                    c.Nullable ? "null" : "not null");
            }

            sb.Append(");");
            db.SetCommand(sb.ToString()).ExecuteNonQuery();
        }
    }
}
